
#include <stdio.h>
#include <math.h>

#include "DataContainer.h"

using namespace std;

DataContainer::DataContainer() : data(NULL), proc_map(NULL), kdTreePtr(NULL),
  dim_x(0), dim_y(0), num_procs(0) {}

#if 0
DataContainer::DataContainer(int blk_size_x, int blk_size_y, \
    int dim_x, int dim_y, int num_procs) : \
    blk_size_x(blk_size_x), blk_size_y(blk_size_y), \
    dim_x(dim_x), dim_y(dim_y), num_procs(num_procs) {
  
  instantiateContainer();
  partitionAndGenerateOffsets();  
}
#endif

DataContainer::~DataContainer() {
  // Delete the KdTree pointer
  if (kdTreePtr) {  
    delete kdTreePtr;
    kdTreePtr = NULL;
  }

  // Delete 'data' container
  if (data) {
    for (int i=0; i<dim_y; i++) { 
      delete [] data[i];
      data[i] = NULL;
    }
  }

  delete [] data;
  data = NULL;
 
#if 0 
  // Delete the processor map
  for (int i=0; i<num_blks_y; i++) 
    delete [] proc_map[i];

  delete [] proc_map;
#endif
}

void DataContainer::instantiate(int dim_x, int dim_y, int num_procs) {
  // Just for disambiguation
  // this->blk_size_x = blk_size_x;
  // this->blk_size_y = blk_size_y;
  
  this->dim_x = dim_x;
  this->dim_y = dim_y;

  this->num_procs = num_procs;

  partitionAndGenerateOffsets();
}

void DataContainer::allocate() {
  // Is there a better way of doing this???
  data = new CONTAINER_TYPE* [dim_y];
  for (int i=0; i<dim_y; i++) 
    data[i] = new CONTAINER_TYPE [dim_x];
}

// void DataContainer::fillContainer() {
// }

void DataContainer::partitionAndGenerateOffsets() {
  kdTreePtr = new KdTree(INT_PAIR(dim_x, dim_y), num_procs);
  proc_map = kdTreePtr->getProcAllocTable();
      
#if 0
  printf("proc_alloc_table.size(): %ld\n", proc_alloc_table.size());  

  for (int i=0; i<proc_alloc_table.size(); i++) {
    for (int j=0; j<proc_alloc_table[i].size(); j++) {
      printf("%d \t", proc_alloc_table[i][j]);  
    }
    printf("\n");
  }
#endif

#if 0
  num_blks_x = (int)ceil((double)dim_x/blk_size_x);
  num_blks_y = (int)ceil((double)dim_y/blk_size_y);
#else
  num_blks_x = kdTreePtr->getNumBlocksX(); 
  num_blks_y = kdTreePtr->getNumBlocksY(); 
#endif
  
  // printf("num_blks_y: %d\n", num_blks_y); 

  // Initialize the offset map
  offset_map.resize(num_blks_y); 
  for (int i=0; i<num_blks_y; i++) {
    offset_map[i].resize(num_blks_x);
  }

  // Populate the offset map
#if 0
  int proc_count = 0, offset_x = 0, offset_y = 0;
  for (int i=0; i<num_blks_y; i++, offset_x=0) {
    for (int j=0; j<num_blks_x; j++) {
      if (proc_count>=num_procs)
	proc_count = 0;

      proc_map[i][j] = proc_count;
      proc_count++;

      offset_map[i][j] = INT_PAIR(offset_x, offset_y);
      offset_x += blk_size_x;
    }

    offset_y += blk_size_y;
  }
#else
  for (int i=0; i<num_blks_y; i++) {
    for (int j=0; j<num_blks_x; j++) {
      const KdTreeNode *node = kdTreePtr->getProcNode(proc_map[i][j]);
      if (node)
        offset_map[i][j] = node->min;
    }
  }
#endif
}

const vector< vector<int> >& DataContainer::getProcMap() const {
  return proc_map;
}
   
const vector< vector<INT_PAIR>  >& DataContainer::\
    getOffsetMap() const {
  return offset_map;
}

int DataContainer::getNumBlocksX() const {
  return num_blks_x;
}

int DataContainer::getNumBlocksY() const {
  return num_blks_y;
}

int DataContainer::getBlockSizeX(int i, int j) const {
  int proc = proc_map[i][j];
  const KdTreeNode *node = kdTreePtr->getProcNode(proc);
  if (node) {
    INT_PAIR max = node->max;
    INT_PAIR min = node->min;
    return (max[0]-min[0]);
  }
  else 
    return -1;
}

int DataContainer::getBlockSizeY(int i, int j) const {
  int proc = proc_map[i][j];
  const KdTreeNode *node = kdTreePtr->getProcNode(proc);
  if (node) {
    INT_PAIR max = node->max;
    INT_PAIR min = node->min;
    return (max[1]-min[1]);
  }
  else 
    return -1;
}

CONTAINER_TYPE const** DataContainer::getData() const {
  return (CONTAINER_TYPE const**)(data);
}
